package small.common.designPattern.structure.proxy.jdk;


import small.common.core.util.ClassUtil;
import small.common.designPattern.structure.proxy.AdvisedSupport;
import small.common.designPattern.structure.proxy.AopMethodInterceptor;
import small.common.designPattern.structure.proxy.AopProxy;
import small.common.designPattern.structure.proxy.ReflectiveMethodInvocation;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @Author zhoujin
 * @Date 2022/6/26 15:46
 */
public class JdkDynamicAopProxy implements AopProxy {

    public static final AopProxy JDK_PROXY = new JdkDynamicAopProxy();

    private JdkDynamicAopProxy() {

    }

    @SuppressWarnings("unchecked")
    @Override
    public <T> T getProxy(AdvisedSupport<T> adviseSupport) {
        return (T) Proxy.newProxyInstance(ClassUtil.getDefaultClassLoader(), adviseSupport.getTargetSource().getInterfaces(), new JdkMethodInvocation(adviseSupport));
    }

    private static class JdkMethodInvocation implements InvocationHandler {

        private final AdvisedSupport adviseSupport;

        public JdkMethodInvocation(AdvisedSupport adviseSupport) {
            this.adviseSupport = adviseSupport;
        }

        @Override
        public Object invoke(Object o, Method method, Object[] args) throws Throwable {
            Object target = adviseSupport.getTargetSource().getTarget();
            AopMethodInterceptor aopMethodInterceptor = adviseSupport.getAopMethodInterceptor();
            if (adviseSupport.getMethodMatcher() != null) {
                if (adviseSupport.getMethodMatcher().matches(method, adviseSupport.getTargetSource().getTargetClass())) {
                    return aopMethodInterceptor.invoke(new ReflectiveMethodInvocation(target, adviseSupport.getTargetSource().getTargetClass(), method, args));
                } else {
                    if (target == null) {
                        return null;
                    }
                    //执行原来方法
                    return method.invoke(target, args);
                }

            }
            return aopMethodInterceptor.invoke(new ReflectiveMethodInvocation(target, adviseSupport.getTargetSource().getTargetClass(), method, args));
        }
    }
}
